<template>
  <div class="commonTable">
    <div class="comTableHeader">
      <slot name="tool"></slot>
    </div>
    <div class="comTableBody" v-loading="mainLoading || loading">
      <el-table
        ref="elTable"
        :data="pageData"
        :height="mainConfig.height"
        :max-height="mainConfig.maxHeight"
        :stripe="mainConfig.stripe"
        :border="mainConfig.border"
        :size="mainConfig.size"
        :fit="mainConfig.fit"
        :show-header="mainConfig.showHeader"
        :highlight-current-row="mainConfig.highlightCurrentRow"
        :current-row-key="mainConfig.currentRowKey"
        :row-key="mainConfig.rowKey"
        :row-class-name="mainConfig.rowClassName"
        :row-style="mainConfig.rowStyle"
        :cell-class-name="mainConfig.cellClassName"
        :cell-style="mainConfig.cellStyle"
        :header-row-class-name="mainConfig.headerRowClassName"
        :header-row-style="mainConfig.headerRowStyle"
        :tooltip-effect="mainConfig.tooltipEffect"
        :select-on-indeterminate="mainConfig.selectOnIndeterminate"
        :empty-text="mainConfig.emptyText"
        @current-change="tableCurrentChange"
        @selection-change="tableSelectionChange"
        @cell-click="tableCellClick"
        @row-click="tableRowClick"
        @header-click="tableHeaderClick"
      >
        <!-- 
                    默认插槽 
                    多选列除外  请尽量使用props 多选列进行配置 保证统一性
                    1. columns 列配置如果存在则根据列配置进行渲染  
                    2. 不使用列配置直接使用 el-table-column进行配置 
                -->
        <slot>
          <template v-for="(colItem, colIndex) in columns">
            <!-- 二次封装组件  只支持部分基础属性  如果需要完整的功能  直接使用默认插槽进行渲染 -->
            <Column :config="colItem" :key="colIndex" />
          </template>
        </slot>
      </el-table>
    </div>
    <div
      class="comTablePage"
      :class="['comTablePage-position-' + mainConfig.paginationPostion]"
    >
      <el-pagination
        :current-page.sync="page"
        :page-count="pageTotal"
        :page-sizes="mainConfig.pageSizes"
        :page-size="mainConfig.pageSize"
        :layout="mainConfig.layout"
        @size-change="pageSizeChange"
        @current-change="pageCurrentChange"
      >
      </el-pagination>
    </div>
    <!-- card body -->
  </div>
</template>

<script>
import Column from "./Column"
import easyConfig from "./config"
export default {
  name: "EasyTable",
  props: {
    // 在组件生命周期 何时进行一次初始化
    initLife: {
      type: String,
      default: "created"
    },
    // 初始页码
    initPage: {
      type: Number,
      default: 1
    },
    // 表格基础配置
    // 常用基础选项
    options: {
      type: Object,
      default() {
        // api同el-table  el-pagination组件配置项一致 将于组件默认配置进行合并  驼峰写法
        return {}
      },
      required: false
    },
    // 列配置
    columns: {
      type: Array,
      required: true,
      default() {
        return []
      }
    },
    // 数据接口函数
    fetchFun: {
      type: Function,
      required: false
    },
    // 传入的表格data内容
    data: {
      type: Array,
      required: false,
      default() {
        return []
      }
    },
    // 查询关键字
    query: {
      type: [Object, String],
      required: false,
      default: ""
    },
    // 对接口响应数据进行二次编辑
    // 针对不同项目的接口很难保证统一性 建议放在config里面进行编辑
    responseFilter: {
      type: Function,
      required: false,
      default: easyConfig.responseFilter
    },
    // 是否加载中 二级辅助控制 可控制el-table v-loading是否显示
    // 当 fetchFun 处于加载过程中  内部_loading才是主控制权
    loading: {
      type: Boolean,
      required: false,
      default: false
    }
  },
  components: {
    Column
  },
  created() {
    if (this.initLife === "created") {
      this.fetchData()
    }
  },
  mounted() {
    if (this.initLife === "mounted") {
      this.fetchData()
    }
  },
  data() {
    return {
      // 合并配置项
      mainConfig: this.getMergeOptions(),
      // 是否加载中 - 主
      mainLoading: false,
      // 当前页码 - 初始页码通过配置项获取
      page: this.initPage || 1,
      // 总页码
      pageTotal: 1,
      // 表格内容
      pageData: [],
      // 选中的表格行列表
      selectionRows: [],
      // 高亮选中的表格行
      currentRow: ""
    }
  },
  methods: {
    // 初始化table options
    getMergeOptions(optionsArg) {
      return Object.assign(easyConfig.options, optionsArg || this.options)
    },
    // 获取element-table组件实例
    getTableRef() {
      return this.$refs.elTable
    },
    // 获取表格基础配置项
    getTableOptions() {
      return {
        page: this.page,
        pageData: this.pageData,
        pageSize: this.mainConfig.pageSize,
        query: this.query
      }
    },
    // 获取表格数据的方式
    getTableDataType() {
      if (this.fetchFun) {
        return "fetch"
      }
      if (this.data) {
        return "data"
      }
    },
    // 设置表格页码
    setPage(pageArg = this.page) {
      this.pageSize = pageArg
      return this.fetchData()
    },
    // 设置表格页码列表数量
    setPageSize(pageSizeArg = this.mainConfig.pageSize) {
      if (pageSizeArg) {
        const hasPageSizeArg = this.mainConfig.pageSizes.includes(pageSizeArg)
        if (hasPageSizeArg) {
          this.mainConfig.pageSize = pageSizeArg
        } else {
          this.mainConfig.pageSizes.push(pageSizeArg)
          this.mainConfig.pageSize = pageSizeArg
        }
        return this.fetchData()
      }
    },
    // 获取多选模式下行在原数据中的索引   主要针对传 data的模式下
    // data模式下  通过页码 页数量 当前列表索引  优化查询索引速度
    // fetch模式下 直接返回当前页索引
    // 第一个参数为查询key唯一值 第二个参数为返回值是否为倒序  默认为 倒序  方便后续删除操作
    getSelectionIndex(findKey = this.mainConfig.rowKey, reverseAuto = true) {
      if (this.selectionRows.length) {
        const inDataIndexs = []
        const curPageFirstIndex = (this.page - 1) * this.mainConfig.pageSize
        const getDataType = this.getTableDataType()
        for (const selItem of this.selectionRows) {
          const findSelIndex = this.pageData.findIndex((rowItem) => {
            return rowItem[findKey] === selItem[findKey]
          })
          if (findSelIndex !== -1) {
            if (getDataType === "data") {
              inDataIndexs.push(curPageFirstIndex + findSelIndex)
            } else {
              inDataIndexs.push(findSelIndex)
            }
          } else {
            throw Error(`Not find the key[${selItem[findKey]}]`)
          }
        }
        if (reverseAuto) {
          return inDataIndexs.reverse()
        } else {
          return inDataIndexs
        }
      } else {
        return []
      }
    },
    // 删除多选模式下选中的行
    // 返回一个promise对象
    // refreshAuto参数  删除完成之后是否自动刷新表格数据
    delSelection(refreshAuto = true) {
      return new Promise((resolve, reject) => {
        if (this.selectionRows) {
          const delRowsIndex = this.getSelectionIndex()
          const getDataType = this.getTableDataType()

          const originData = getDataType === "data" ? this.data : this.pageData
          if (!refreshAuto || getDataType === "data") {
            delRowsIndex.forEach((rowIndex) => {
              originData.splice(rowIndex, 1)
            })
          }

          const delRows = this.selectionRows.slice()
          if (refreshAuto) {
            this.setPage()
              .then(() => {
                resolve(delRows)
              })
              .catch((err) => {
                reject(err)
              })
          } else {
            resolve(delRows)
          }
        } else {
          reject("selectionRows length is null")
        }
      })
    },
    // 获取单选模式下高亮行在原数据中的索引
    // 如果没有高亮行  则返回 -1
    getCurrentRowIndex() {
      if (this.currentRow) {
        const getDataType = this.getTableDataType()
        const inPageDataIndex = this.pageData.findIndex((rowItem) => {
          return (
            rowItem[this.mainConfig.rowKey] ===
            this.currentRow[this.mainConfig.rowKey]
          )
        })
        if (getDataType === "fetch") {
          return inPageDataIndex
        } else {
          const curPageFirstIndex = (this.page - 1) * this.mainConfig.pageSize
          return curPageFirstIndex + inPageDataIndex
        }
      } else {
        return -1
      }
    },
    // 删除单选模式下的高亮行
    // 返回一个promise对象
    // refreshAuto参数  删除完成之后是否自动刷新表格数据
    delCurrentRow(refreshAuto = true) {
      return new Promise((resolve, reject) => {
        if (this.currentRow) {
          const currentRowIndex = this.getCurrentRowIndex()
          if (currentRowIndex !== -1) {
            const delRow = this.currentRow
            const getDataType = this.getTableDataType()
            const originData =
              getDataType === "data" ? this.data : this.pageData
            if (!refreshAuto || getDataType === "data") {
              originData.splice(currentRowIndex, 1)
            }
            this.getTableRef().setCurrentRow()
            if (refreshAuto) {
              this.setPage()
                .then(() => {
                  resolve(delRow)
                })
                .catch((err) => {
                  reject(err)
                })
            } else {
              resolve(delRow)
            }
          } else {
            reject("not find the currentRow")
          }
        } else {
          reject("currentRow is not defined")
        }
      })
    },
    //获取表格内容
    // 两种模式
    // 1. 当fetchFun存在的情况下 用接口获取指定页码内容 优先级最大
    // 2. 当data存在的情况下  截取指定指定页码内容
    // fetchArg Object  { page 页码 , pageSize 数量 , query 查询关键字 }
    // viewAutoArg Booolean 是否对视图进行渲染 默认渲染视图 否则只返回获取的数据
    fetchData(fetchArg, viewAutoArg = true) {
      return new Promise((resolve, reject) => {
        fetchArg = Object.assign(
          {
            page: this.page,
            pageSize: this.mainConfig.pageSize,
            query: this.query
          },
          fetchArg
        )
        if (this.fetchFun) {
          if (typeof this.fetchFun !== "function") {
            throw Error("Props fetchFun Type must be Function")
          }
          if (viewAutoArg) {
            this.mainLoading = true
          }
          this.fetchFun(fetchArg)
            .then((res) => {
              // 如果数据响应过滤函数存在则进行一边数据过滤  主要针对不同接口的数据返回情况提供一个可编辑的接口
              if (
                this.responseFilter &&
                typeof this.responseFilter === "function"
              ) {
                res = this.responseFilter(res)
              }
              // 对当前页内容 当前页 总页码进行渲染
              const resData = res.data
              const fetchResult = {
                page: Math.min(fetchArg.page, this.pageTotal),
                pageData: resData.list,
                pageTotal: resData.total
              }
              if (viewAutoArg) {
                Object.assign(this, fetchResult)
              }
              resolve(fetchResult)
              // 主要看后台逻辑 默认请求页码大于最大页码情况下   在进行一次请求获取争取页码的数据
              if (fetchArg.page > this.pageTotal) {
                this.fetchData({ page: this.page })
              }
            })
            .catch((err) => {
              reject(err)
            })
            .finally(() => {
              if (viewAutoArg) {
                this.mainLoading = false
              }
            })
        } else {
          if (this.data) {
            if (!Array.isArray(this.data)) {
              throw Error("Props data Type must be Array")
            }
            const fetchResult = {
              pageTotal: Math.ceil(this.data.length / this.mainConfig.pageSize),
              page: Math.min(fetchArg.page, this.pageTotal),
              pageData: this.data.slice(
                (this.page - 1) * this.mainConfig.pageSize,
                this.page * this.mainConfig.pageSize
              )
            }
            if (viewAutoArg) {
              this.mainLoading = true
              Object.assign(this, fetchResult)
              this.mainLoading = false
            }
            resolve(fetchResult)
          } else {
            throw Error("Props data is not defined")
          }
        }
      })
    },
    // 开启多选模式下监听选中的行
    tableSelectionChange(rows) {
      this.selectionRows = rows
      this.$emit("selection-change", rows)
    },
    // 开启高亮选择情况下  高亮当前行的选项变化
    tableCurrentChange(row) {
      this.currentRow = row
      this.$emit("current-change", row)
    },
    // 页码变化
    pageCurrentChange(page) {
      this.page = page
      this.setPage(page)
      this.$emit("page-change", page)
    },
    // 页码数量变化
    pageSizeChange(size) {
      this.mainConfig.pageSize = size
      this.setPageSize(size)
      this.$emit("size-change", size)
    },
    // 当某个单元格被点击时会触发该事件
    tableRowClick(row, column, event) {
      this.$emit("row-click", { row, column, event })
    },
    // 当某个单元格被点击时会触发该事件
    tableCellClick(row, column, cell, event) {
      this.$emit("cell-click", { row, column, cell, event })
    },
    // 当某一列的表头被点击时会触发该事件
    tableHeaderClick(column, event) {
      this.$emit("header-click", { column, event })
    },
    // 当表格的筛选条件发生变化的时候会触发该事件
    tableSortChange({ column, prop, order }) {
      this.$emit("sort-change", { column, prop, order })
    },
    // 当表格的筛选条件发生变化的时候会触发该事件
    tableFilterChange(filters) {
      this.$emit("filter-change", { filters })
    }
  },
  watch: {
    // 监听 配置项变化  对配置项进行重新渲染 针对大多数配置项  并不是所有
    options: {
      deep: true,
      handler(newVal) {
        this.mainConfig = this.getMergeOptions(newVal)
      }
    }
  }
}
</script>
<style lang="scss" scoped>
// .comTable{

// }
.comTableHeader {
  padding-bottom: 20px;
}
.comTablePage {
  display: flex;
  align-items: center;
  padding-top: 20px;
}

.comTablePage-position-left {
  justify-content: flex-start;
}

.comTablePage-position-center {
  justify-content: center;
}

.comTablePage-position-right {
  justify-content: flex-end;
}
</style>
